package com.pimee.support.shiro.credentials;

import java.util.concurrent.TimeUnit;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import com.pimee.common.constants.Constants.CacheKey;
import com.pimee.common.util.SpringContextUtil;
import com.pimee.model.SysUser;
import com.pimee.service.admin.ISysUserService;

/**
 * Shiro-密码验证管理-登录错误计数,（闲麻烦也可以使用HashedCredentialsMatcher也行，配置中我已经加好了代码)
 * 
 **/
public class RetryLimitCredentialsMatcher extends CredentialsMatcher {

	private static final Logger log = LoggerFactory.getLogger(RetryLimitCredentialsMatcher.class);

	
	/**
	 * 用户登录次数计数 redisKey 前缀
	 */
	private static final String SHIRO_LOGIN_COUNT = "shiro:login:count:";
	/**
	 * 用户登录是否被锁定 一小时 redisKey 前缀
	 */
	private static final String SHIRO_IS_LOCK = "shiro:is:lock:";

	@Autowired
	private RedisTemplate<String, String> redisTemplate;
	
	@Override
	public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
		
		ISysUserService sysUserService =  (ISysUserService) SpringContextUtil.getBean("sysUserService");
		
		SysUser sysUser = (SysUser) info.getPrincipals().getPrimaryPrincipal();

		String account = sysUser.getLoginName();

		// 访问计数
		ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
		String loginCountKey = SHIRO_LOGIN_COUNT + account;
		String isLockKey = SHIRO_IS_LOCK + account;
		valueOperations.increment(loginCountKey, 1);

		// 如果禁止登陆，提示
		if (redisTemplate.hasKey(isLockKey)) {
			throw new ExcessiveAttemptsException("sorry，账号【" + account + "】已被禁止1小时内登陆！");
		}

		// 根据计数锁定用户
		String logcounts = String.valueOf(valueOperations.get(loginCountKey));
		int i = (5 - Integer.parseInt(logcounts));

		// 如果超过5次，就禁止一个小时
		if (i <= 0) {
			valueOperations.set(isLockKey, "LOCK");
			redisTemplate.expire(isLockKey, 1, TimeUnit.HOURS);
			redisTemplate.expire(loginCountKey, 1, TimeUnit.HOURS);
			throw new ExcessiveAttemptsException("sorry,密码输出错误次数太多，账号【" + account + "】已被禁止登录！");
		}

		// 添加提示信息
		Boolean aBoolean = super.doCredentialsMatch(token, info);
		if (!aBoolean) {
			String message = i <= 0 ? "你的账号禁止1小时内登陆" : "你还剩【" + i + "】次重试的机会！";
			throw new AccountException("账号或密码不正确！" + message);
		}

		// 初始化登录计数
		redisTemplate.delete(loginCountKey);

		// 更新用户最后一次登录状态
		try {
			sysUserService.updateUser(sysUser);
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 但验证全部通过之后，将用户信息存在session里面
		SecurityUtils.getSubject().getSession().setAttribute(CacheKey.USER_SESSION_KEY, sysUser);
		log.info("[用户验证通过，用户名:]-[{}]", account);
		return true;
	}
}
